package org.ecloud.oauth.server.provider;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.ecloud.common.constants.StateEnum;
import org.ecloud.core.entity.APIResult;
import org.ecloud.encrypt.EncryptUtil;
import org.ecloud.oauth.constants.GrantType;
import org.ecloud.oauth.constants.RedisKey;
import org.ecloud.oauth.entity.TokenEntity;
import org.ecloud.oauth.server.entity.ClientVo;
import org.ecloud.oauth.server.entity.UserVo;
import org.ecloud.redis.utils.RedisUtil;
import org.ecloud.utils.BeanUtil;
import org.ecloud.utils.DateUtil;
import org.ecloud.utils.StringUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

/** 
 * auth2.0 访问令牌控制器
 */
@Api(tags = "认证中心", value = "token")
@RestController
@RequestMapping("/authentication")
public class TokenProvider extends BaseProvider {

	/*
	-----------------------------------------------------------------
	请求参数
 	参数名			必选	类型及范围	说明
	grant_type		true	string	请求的类型，填写authorization_code/password_credentials/client_credentials/refresh_token/implicit
	-----------------------------------------------------------------
	grant_type为authorization_code时
	参数名			必选		类型及范围	说明
	client_id		true	string	申请应用时分配的AppKey。
	client_secret	true	string	申请应用时分配的AppSecret。
	authorize_code	true	string	调用authorize获得的code值。
	redirect_uri	false	string	回调地址，需需与注册应用里的回调地址一致。
	-----------------------------------------------------------------
	grant_type为client_credentials时
	参数名			必选		类型及范围	说明
	client_id		true	string	申请应用时分配的AppKey。
	client_secret	true	string	申请应用时分配的AppSecret。
	-----------------------------------------------------------------
	grant_type为password_credentials时
	参数名			必选		类型及范围	说明
	username		true	string	用户名。
	password		true	string	密码。
	-----------------------------------------------------------------
	grant_type为refresh_token时
	参数名			必选		类型及范围	说明
	refresh_token	true	string	续约token。
	-----------------------------------------------------------------
	返回数据
	 {
	       "access_token": "ACCESS_TOKEN",
	       "expires_in": 1800,
	       "refresh_token": "REFRESH_TOKEN",
	       "remind_in": 3600,
	       "uid": "1"
	 }
	-----------------------------------------------------------------
	返回值字段			字段类型	字段说明
	access_token	string	用户授权的唯一票据，用于调用微博的开放接口，同时也是第三方应用验证微博用户登录的唯一票据，第三方应用应该用该票据和自己应用内的用户建立唯一影射关系，来识别登录状态，不能使用本返回值里的UID字段来做登录识别。
	expires_in		string	access_token的生命周期，单位是秒数。
	refresh_token	string	只能用于token续约
	remind_in		string	refresh_token的生命周期，单位是秒数。
	uid				string	授权用户的UID，本字段只是为了方便开发者，减少一次user/show接口调用而返回的，第三方应用不能用此字段作为用户登录状态的识别，只有access_token才是用户授权的唯一票据。 
	*/
	
	/**
	 * 获取访问token
	 * 
	 * <pre>
	 * https://api.weibo.com/oauth2/access_token?
	 * 		client_id=YOUR_CLIENT_ID&
	 * 		client_secret=YOUR_CLIENT_SECRET&
	 * 		grant_type=authorization_code&
	 * 		redirect_uri=YOUR_REGISTERED_REDIRECT_URI&
	 * 		code=CODE
	 * 
	 * https://api.weibo.com/oauth2/access_token?
	 * 		client_id=YOUR_CLIENT_ID&
	 * 		client_secret=YOUR_CLIENT_SECRET&
	 * 		grant_type=refresh_token&
	 * 		redirect_uri=YOUR_REGISTERED_REDIRECT_URI&
	 * 		refresh_token=SlAV32jfydWG
	 * 
	 * {
	 *	    "access_token": "SlAV32hkKG",
	 *	    "expires_in": 3600,
	 *	    "refresh_token": "SlAV32jfydWG",
	 *	    "remind_in": 3600,
	 *		"uid": "1"
	 *	}
	 * </pre>
	 */
	@ApiOperation(value = "申请access_token", notes = "传入授权AppKey，申请access_token")
	@RequestMapping(value = "", method = RequestMethod.POST)
	public APIResult<TokenEntity> accessToken(
			@ApiParam(name = "grant_type", value = "请求类型", required = true) @RequestParam(name = "grant_type", required = true) String grantType,
			@ApiParam(name = "client_id", value = "申请应用时分配的AppKey", required = false) @RequestParam(name = "client_id", required = false) String clientId,
			@ApiParam(name = "client_secret", value = "申请应用时分配的AppSecret", required = false) @RequestParam(name = "client_secret", required = false) String clientSecret,
			@ApiParam(name = "username", value = "用户名", required = false) @RequestParam(name = "username", required = false) String username,
			@ApiParam(name = "password", value = "密码", required = false) @RequestParam(name = "password", required = false) String password,
			@ApiParam(name = "refresh_token", value = "调用access_token获得的refresh_token值", required = false) @RequestParam(name = "refresh_token", required = false) String refreshToken,
			@ApiParam(name = "authorize_code", value = "调用authorize获得的code值", required = false) @RequestParam(name = "authorize_code", required = false) String authorizeCode,
			@ApiParam(name = "redirect_uri", value = "回调地址", required = false) @RequestParam(name = "redirect_uri", required = false) String redirectUri
			) {
		logger.debug("request access_token");
		APIResult<TokenEntity> result = new APIResult<>();
		try{
			TokenEntity token = new TokenEntity();
			logger.debug("grant type is {}", grantType);
			if(GrantType.AUTHORIZATION_CODE.equalsIgnoreCase(grantType)){
				genTokenByAuthorizationCode(clientId, clientSecret, authorizeCode, result, token);
			}else if(GrantType.PASSWORD_CREDENTIALS.equalsIgnoreCase(grantType)){
				genTokenByPasswordCredentials(username, password, result, token);
			} else if(GrantType.CLIENT_CREDENTIALS.equalsIgnoreCase(grantType)){ 
				genTokenByClientCredentials(clientId, clientSecret, result, token);
			} else if(GrantType.REFRESH_TOKEN.equalsIgnoreCase(grantType)){ 
				genTokenByRefreshToken(username, password, clientId, clientSecret, refreshToken, result, token);
			} else {
				result.setCode(StateEnum.ILLEGAL_GRANT_TYPE.getCode());
				throw new RuntimeException(StateEnum.ILLEGAL_GRANT_TYPE.getText());
			}
			
			result.setData(token);
		}catch(Exception e){
			if(StateEnum.SUCCESS.getCode() == result.getCode()){
				result.setCode(StateEnum.ILLEGAL_REQUEST.getCode());
			}
			//result.setMessage("");
			result.setCause(e.getMessage());
			logger.error("access token failed:", e);
		}
		
		return result;
	}

	private void genTokenByRefreshToken(String username, String password, String clientId, String clientSecret, String refreshToken,
			APIResult<TokenEntity> result, TokenEntity token) {
		// 0、校验参数合法性，refresh_token/client_id/client_secret；
		// 1、缓存中取出用户信息；
		// 2、生成token返回；
		
		if(StringUtil.isBlank(refreshToken)){
			result.setCode(StateEnum.ILLEGAL_TOKEN.getCode());
			throw new RuntimeException("非法token");
		}
		
		String rtkey = appConfig.getRedisKey(RedisKey.REFRESH_TOKEN, refreshToken);
		if(!RedisUtil.redisTemplate.hasKey(rtkey)){
			result.setCode(StateEnum.ILLEGAL_TOKEN.getCode());
			throw new RuntimeException("非法token");
		}
		
		Object gtObject = RedisUtil.redisTemplate.opsForHash().get(rtkey, RedisKey.GRANT_TYPE);
		Object atObject = RedisUtil.redisTemplate.opsForHash().get(rtkey, RedisKey.ACCESS_TOKEN);
		Object cidObject = RedisUtil.redisTemplate.opsForHash().get(rtkey, RedisKey.CLIENT_ID);
		Object csObject = RedisUtil.redisTemplate.opsForHash().get(rtkey, RedisKey.CLIENT_SECRET);
		Object unObject = RedisUtil.redisTemplate.opsForHash().get(rtkey, RedisKey.USER_NAME);
		Object pObject = RedisUtil.redisTemplate.opsForHash().get(rtkey, RedisKey.PASSWORD);
		
		if(BeanUtil.isEmpty(gtObject)){
			result.setCode(StateEnum.ILLEGAL_LOGIN_STATE.getCode());
			throw new RuntimeException("用户未登录");
		}
		
		// 不同授权类型不同操作
		String gt = gtObject.toString();
		String cid = null;
		String cs = null;
		String un = null;
		String p = null;
		if(GrantType.AUTHORIZATION_CODE.equalsIgnoreCase(gt)){
			if(tokenConfig.isRefreshTokenValidComplex()){
				if(StringUtil.isBlank(clientId) || StringUtil.isBlank(clientSecret)){
					result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
					throw new RuntimeException("授权信息不完整");
				}
				
				cid = cidObject.toString();
				cs = csObject.toString();
				if(!clientId.equals(cid) || !clientSecret.equals(cs)){
					result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
					throw new RuntimeException("授权信息不匹配");
				}
			}
			
			un = unObject.toString();
			UserVo user = userService.getByAccount(un);
			if(BeanUtil.isEmpty(user)){
				result.setCode(StateEnum.ILLEGAL_ACCOUNT_PASSWORD.getCode());
				throw new RuntimeException("用户信息不完整");
			}
			
			token.setUid(user.getUid());
		}else if(GrantType.PASSWORD_CREDENTIALS.equalsIgnoreCase(gt)){
			un = unObject.toString();
			if(tokenConfig.isRefreshTokenValidComplex()){
				if(StringUtil.isBlank(username) || StringUtil.isBlank(password)){
					result.setCode(StateEnum.ILLEGAL_ACCOUNT_PASSWORD.getCode());
					throw new RuntimeException("用户信息不完整");
				}
				
				p = pObject.toString();
				if(!username.equals(un) || !EncryptUtil.encryptSha256(password).equals(p)){
					result.setCode(StateEnum.ILLEGAL_ACCOUNT_PASSWORD.getCode());
					throw new RuntimeException("用户信息不匹配");
				}
			}
			
			UserVo user = userService.getByAccount(un);
			if(BeanUtil.isEmpty(user)){
				result.setCode(StateEnum.ILLEGAL_ACCOUNT_PASSWORD.getCode());
				throw new RuntimeException("用户信息不完整");
			}
			
			token.setUid(user.getUid());
		} else if(GrantType.CLIENT_CREDENTIALS.equalsIgnoreCase(gt)){
			if(tokenConfig.isRefreshTokenValidComplex()){
				if(StringUtil.isBlank(clientId) || StringUtil.isBlank(clientSecret)){
					result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
					throw new RuntimeException("授权信息不完整");
				}
				
				cid = cidObject.toString();
				cs = csObject.toString();
				if(!clientId.equals(cid) || !clientSecret.equals(cs)){
					result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
					throw new RuntimeException("授权信息不匹配");
				}
			}
		} else {
			result.setCode(StateEnum.ILLEGAL_REQUEST.getCode());
			throw new RuntimeException("请求类型不支持！");
		}
		
		token.setAccess_token(uuid());
		token.setExpires_in(tokenConfig.getExpires());
		token.setRefresh_token(uuid());
		token.setRemind_in(tokenConfig.getRemind());
		
		// 删除两个token
		String atkey = appConfig.getRedisKey(RedisKey.ACCESS_TOKEN, atObject.toString());
		RedisUtil.redisTemplate.delete(atkey);
		RedisUtil.redisTemplate.delete(rtkey);
		// 存入redis
		atkey = appConfig.getRedisKey(RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.GRANT_TYPE, gt);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		rtkey = appConfig.getRedisKey(RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.GRANT_TYPE, gt);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.ACCESS_TOKEN, token.getAccess_token());
		
		if(GrantType.AUTHORIZATION_CODE.equalsIgnoreCase(gt)){
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_ID, cid);
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_SECRET, cs);
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.USER_NAME, un);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_ID, cid);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_SECRET, cs);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.USER_NAME, un);
		}else if(GrantType.PASSWORD_CREDENTIALS.equalsIgnoreCase(gt)){
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.USER_NAME, un);
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.PASSWORD, p);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.USER_NAME, un);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.PASSWORD, p);
		} else if(GrantType.CLIENT_CREDENTIALS.equalsIgnoreCase(gt)){
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_ID, cid);
			RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_SECRET, cs);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_ID, cid);
			RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_SECRET, cs);
		}
		
		RedisUtil.redisTemplate.expire(atkey, tokenConfig.getExpires(), TimeUnit.SECONDS);
		RedisUtil.redisTemplate.expire(rtkey, tokenConfig.getRemind(), TimeUnit.SECONDS);
		
		// 单端登录
		if(GrantType.AUTHORIZATION_CODE.equalsIgnoreCase(gt)){
			String key = appConfig.getRedisKey(RedisKey.KEY, un);
			if(tokenConfig.getAuthorizationCode().isSingle()){
				cleanAndCacheKeys(key, atkey, rtkey);
			}
		} else if(GrantType.PASSWORD_CREDENTIALS.equalsIgnoreCase(gt)){
			String key = appConfig.getRedisKey(RedisKey.KEY, un);
			if(tokenConfig.getPassword().isSingle()){
				cleanAndCacheKeys(key, atkey, rtkey);
			}
		} else if(GrantType.CLIENT_CREDENTIALS.equalsIgnoreCase(gt)){
			String key = appConfig.getRedisKey(RedisKey.KEY, clientId);
			if(tokenConfig.getClient().isSingle()){
				cleanAndCacheKeys(key, atkey, rtkey);
			}
		}
	}

	private void genTokenByAuthorizationCode(String clientId, String clientSecret, String authorizeCode,
			APIResult<TokenEntity> result, TokenEntity token) {
		// 0、校验参数合法性，client_id/client_secret/authorize_code；
		// 1、生成token返回；
		
		if(StringUtil.isBlank(authorizeCode) 
				|| StringUtil.isBlank(clientId) || StringUtil.isBlank(clientSecret)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("授权信息不完整");
		}
		
		String ackey = appConfig.getRedisKey(RedisKey.AUTHORIZE_CODE, authorizeCode);
		if(!RedisUtil.redisTemplate.hasKey(ackey)){
			result.setCode(StateEnum.ILLEGAL_LOGIN_STATE.getCode());
			throw new RuntimeException("用户未登录");
		}
		
		Object cidObject = RedisUtil.redisTemplate.opsForHash().get(ackey, RedisKey.CLIENT_ID);
		Object unObject = RedisUtil.redisTemplate.opsForHash().get(ackey, RedisKey.USER_NAME);
		
		if(BeanUtil.isEmpty(cidObject) || BeanUtil.isEmpty(unObject)){
			result.setCode(StateEnum.ILLEGAL_LOGIN_STATE.getCode());
			throw new RuntimeException("用户未登录");
		}
		
		String cid = cidObject.toString();
		if(!cid.equals(clientId)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("授权信息不匹配");
		}
		
		ClientVo client = clientService.getByClientId(clientId);
		if(BeanUtil.isEmpty(client)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("授权不存在");
		}
		
		// 密钥匹配
		if(!client.getClientsecret().equals(clientSecret)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("密钥错误");
		}
		
		// 授权过期
		if(DateUtil.compare(client.getExpiretime(), new Date())){
			throw new RuntimeException("授权申请已过期");
		}
		
		token.setAccess_token(uuid());
		token.setExpires_in(tokenConfig.getExpires());
		token.setRefresh_token(uuid());
		token.setRemind_in(tokenConfig.getRemind());
		
		String un = unObject.toString();
		UserVo user = userService.getByAccount(un);
		if(BeanUtil.isNotEmpty(user)){
			token.setUid(user.getUid());
		}
		
		// 删除授权码
		RedisUtil.redisTemplate.delete(ackey);
		// 存入redis
		String atkey = appConfig.getRedisKey(RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.GRANT_TYPE, GrantType.AUTHORIZATION_CODE);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_ID, clientId);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_SECRET, clientSecret);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.USER_NAME, un);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.expire(atkey, tokenConfig.getExpires(), TimeUnit.SECONDS);
		String rtkey = appConfig.getRedisKey(RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.GRANT_TYPE, GrantType.AUTHORIZATION_CODE);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_ID, clientId);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_SECRET, clientSecret);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.USER_NAME, un);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.expire(rtkey, tokenConfig.getRemind(), TimeUnit.SECONDS);
		
		// 单端登录
		String key = appConfig.getRedisKey(RedisKey.KEY, un);
		if(tokenConfig.getAuthorizationCode().isSingle()){
			cleanAndCacheKeys(key, atkey, rtkey);
		}
	}

	private void genTokenByClientCredentials(String clientId, String clientSecret, APIResult<TokenEntity> result,
			TokenEntity token) {
		// 0、校验参数合法性，client_id/client_secret
		// 1、生成token返回
		
		if(StringUtil.isBlank(clientId) || StringUtil.isBlank(clientSecret)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("授权ID或授权密钥为空");
		}
		
		ClientVo client = clientService.getByClientId(clientId);
		if(BeanUtil.isEmpty(client)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("授权不存在");
		}
		
		// 密钥匹配
		if(!client.getClientsecret().equals(clientSecret)){
			result.setCode(StateEnum.ILLEGAL_CLIENT_ID_SECRET.getCode());
			throw new RuntimeException("密钥错误");
		}
		
		// 授权过期
		if(DateUtil.compare(client.getExpiretime(), new Date())){
			throw new RuntimeException("授权申请已过期");
		}
		
		token.setAccess_token(uuid());
		token.setExpires_in(tokenConfig.getExpires());
		token.setRefresh_token(uuid());
		token.setRemind_in(tokenConfig.getRemind());
		
		// 存入redis
		String atkey = appConfig.getRedisKey(RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_ID, clientId);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.CLIENT_SECRET, clientSecret);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.expire(atkey, tokenConfig.getExpires(), TimeUnit.SECONDS);
		String rtkey = appConfig.getRedisKey(RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_ID, clientId);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.CLIENT_SECRET, clientSecret);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.expire(rtkey, tokenConfig.getRemind(), TimeUnit.SECONDS);
		
		// 单端登录
		String key = appConfig.getRedisKey(RedisKey.KEY, clientId);
		if(tokenConfig.getClient().isSingle()){
			cleanAndCacheKeys(key, atkey, rtkey);
		}
	}

	private void genTokenByPasswordCredentials(String username, String password, APIResult<TokenEntity> result,
			TokenEntity token) {
		// 0、校验参数合法性，username/password；
		// 1、读取用户数据；
		// 2、密码匹配；
		// 3、生成token返回；
		
		if(StringUtil.isBlank(username) || StringUtil.isBlank(password)){
			result.setCode(StateEnum.ILLEGAL_ACCOUNT_PASSWORD.getCode());
			throw new RuntimeException("用户名或密码为空");
		}
		UserVo user = userService.login(username, password);
		
		token.setAccess_token(uuid());
		token.setExpires_in(tokenConfig.getExpires());
		token.setRefresh_token(uuid());
		token.setRemind_in(tokenConfig.getRemind());
		token.setUid(user.getUid());
		
		// 存入redis
		String atkey = appConfig.getRedisKey(RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.GRANT_TYPE, GrantType.PASSWORD_CREDENTIALS);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.USER_NAME, username);
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.PASSWORD, user.getPassword());
		RedisUtil.redisTemplate.opsForHash().put(atkey, RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.expire(atkey, tokenConfig.getExpires(), TimeUnit.SECONDS);
		String rtkey = appConfig.getRedisKey(RedisKey.REFRESH_TOKEN, token.getRefresh_token());
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.GRANT_TYPE, GrantType.PASSWORD_CREDENTIALS);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.USER_NAME, username);
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.PASSWORD, user.getPassword());
		RedisUtil.redisTemplate.opsForHash().put(rtkey, RedisKey.ACCESS_TOKEN, token.getAccess_token());
		RedisUtil.redisTemplate.expire(rtkey, tokenConfig.getRemind(), TimeUnit.SECONDS);
		
		// 单端登录
		String key = appConfig.getRedisKey(RedisKey.KEY, username);
		if(tokenConfig.getPassword().isSingle()){
			cleanAndCacheKeys(key, atkey, rtkey);
		}
	}

	/**
	 * 校验access_token合法性
	 * 
	 */
	@ApiOperation(value = "校验令牌", notes = "传入令牌")
	@RequestMapping(value = "verify", method = {RequestMethod.GET, RequestMethod.POST})
	public APIResult<String> verify(@ApiParam(name = "access_token", value = "access_token", required = true) @RequestParam(name = "access_token", required = true) String accessToken){
		logger.debug("verify access_token");
		APIResult<String> result = new APIResult<>();
		
		try{
			String atkey = appConfig.getRedisKey(RedisKey.ACCESS_TOKEN, accessToken);
			if(!RedisUtil.redisTemplate.hasKey(atkey)){
				result.setCode(StateEnum.ILLEGAL_TOKEN.getCode());
				throw new RuntimeException("非法token");
			}
			
			Object gtObject = RedisUtil.redisTemplate.opsForHash().get(atkey, RedisKey.GRANT_TYPE);
			Object unObject = RedisUtil.redisTemplate.opsForHash().get(atkey, RedisKey.USER_NAME);
			Object cidObject = RedisUtil.redisTemplate.opsForHash().get(atkey, RedisKey.CLIENT_ID);
			String gt = gtObject.toString();
			result.addVariable(RedisKey.GRANT_TYPE, gt);
			if(GrantType.AUTHORIZATION_CODE.equalsIgnoreCase(gt) 
					|| GrantType.PASSWORD_CREDENTIALS.equalsIgnoreCase(gt)){
				String un = unObject.toString();
				result.setData(un);
			} else if(GrantType.CLIENT_CREDENTIALS.equalsIgnoreCase(gt)){
				String cid = cidObject.toString();
				result.setData(cid);
			} else {
				result.setCode(StateEnum.ILLEGAL_GRANT_TYPE.getCode());
				throw new RuntimeException(StateEnum.ILLEGAL_GRANT_TYPE.getText());
			}
		}catch(Exception e){
			if(StateEnum.SUCCESS.getCode() == result.getCode()){
				result.setCode(StateEnum.ILLEGAL_TOKEN.getCode());
			}
			//result.setMessage("");
			result.setCause(e.getMessage());
			logger.error("access token failed:", e);
		}
		
		return result;
	}
	
}
